#include "md5_nodes.h"

#include <string>
#include <limits>

using namespace krryn::data;

namespace{
	/// lets just say that this is shorter to type :-)
	float g_NaN = std::numeric_limits<float>::quiet_NaN();
}

md5_node_exception::md5_node_exception(const std::string &a_Error)
	: runtime_error(a_Error) {}

md5_vertex::md5_vertex()
	: m_UV(g_NaN, g_NaN),
	  m_WeightStart(-1),
	  m_WeightCount(-1) {}

void md5_vertex::validate(){
	if(m_UV.m_U == g_NaN || m_UV.m_V == g_NaN){
		throw md5_node_exception("Invalid UV coordinate");
	}

	if(m_WeightStart == -1){
		throw md5_node_exception("Invalid weight start");
	}

	if(m_WeightCount == -1){
		throw md5_node_exception("Invalid weight count");
	}
}

md5_triangle::md5_triangle(){
	m_Vertices[0] = -1;
	m_Vertices[1] = -1;
	m_Vertices[2] = -1;
}

void md5_triangle::validate(){
	for(int i = 0; i < 3; i++){
		if(m_Vertices[i] == -1){
			throw md5_node_exception("Invalid index data (triangle)");
		}
	}
}

md5_weight::md5_weight() 
	: m_Joint(invalid_joint), 
	  m_Bias(g_NaN),
	  m_Position(g_NaN, g_NaN, g_NaN) {}

void md5_weight::validate(){
	if(m_Joint == invalid_joint){
		throw md5_node_exception("Invalid joint specified");
	}

	if(m_Bias == g_NaN){
		throw md5_node_exception("Invalid bias specified");
	}

	if(    m_Position.m_X == g_NaN 
		|| m_Position.m_Y == g_NaN 
		|| m_Position.m_Z == g_NaN)
	{
		throw md5_node_exception("Invalid position specified");
	}
}

md5_mesh::md5_mesh()
	: m_NumVertices(-1), m_NumTriangles(-1), m_NumWeights(-1),
  	  m_Vertices(0), m_Triangles(0), m_Weights(0), m_Shader("") {}

md5_mesh::~md5_mesh(){
	delete[] m_Vertices;
	delete[] m_Triangles;
	delete[] m_Weights;
}

void md5_mesh::validate(){
	if(m_NumVertices == -1){
		throw md5_node_exception("No numVertices specified");
	}

	if(m_NumTriangles == -1){
		throw md5_node_exception("No numTriangles specified");
	}

	if(m_NumWeights == -1){
		throw md5_node_exception("No numWeights specified");
	}

	if(m_Shader.empty()){
		throw md5_node_exception("No shader specified");
	}

	for(int i = 0; i < m_NumVertices; i++){
		m_Vertices[i].validate();
	}

	for(int i = 0; i < m_NumTriangles; i++){
		m_Triangles[i].validate();
	}

	for(int i = 0; i < m_NumWeights; i++){
		m_Weights[i].validate();
	}
}

md5_joint::md5_joint()
	: m_Name(""), 
	  m_Parent(invalid_joint), 
	  m_Position(g_NaN, g_NaN, g_NaN),
	  m_Rotation(g_NaN, g_NaN, g_NaN, g_NaN) {}

void md5_joint::validate(){
	if(m_Parent == invalid_joint){
		throw md5_node_exception(
			"No parent joint specified for '" + m_Name + "'");
	}

	if(m_Name.empty()){
		throw md5_node_exception(
			"No name specified for joint for '" + m_Name + "'");
	}

	if(	   m_Position.m_X == g_NaN 
		|| m_Position.m_Y == g_NaN 
		|| m_Position.m_Z == g_NaN)
	{
		throw md5_node_exception(
			"Invalid position specified for '" + m_Name + "'");
	}

	if(	   m_Rotation.m_X == g_NaN 
		|| m_Rotation.m_Y == g_NaN 
		|| m_Rotation.m_Z == g_NaN 
		|| m_Rotation.m_W == g_NaN)
	{
		throw md5_node_exception(
			"Invalid rotation specified for '" + m_Name + "'");
	}
}

md5_animation::md5_animation()
	: m_NumFrames(-1), m_NumJoints(-1), m_FrameRate(-1),
	  m_SkeletalFrames(0), m_BoundingBoxes(0) {}

md5_animation::~md5_animation(){
	// if numjoints is invalid, then the nested 
	// array is not allocated yet so we don't delete it
	if(m_NumJoints != -1){
		for(int i = 0; i < m_NumFrames; i++){
			delete[] m_SkeletalFrames[i];
		}
	}
	delete[] m_SkeletalFrames;
	delete[] m_BoundingBoxes;
}

void md5_animation::validate(){
	if(m_NumFrames <= 0){
		throw md5_node_exception("Invalid number of frames");
	}

	if(m_NumJoints <= 0){
		throw md5_node_exception("Invalid number of joints");
	}

	if(m_FrameRate <= 0){
		throw md5_node_exception("Invalid frame rate");
	}

	for(int i = 0; i < m_NumFrames; i++){
		for(int j = 0; j < m_NumJoints; j++){
			m_SkeletalFrames[i][j].validate();
		}
	}

	for(int i = 0; i < m_NumFrames; i++){
		// m_BoundingBoxes[i].validate();
	}
}

md5_model::md5_model()
	: m_NumMeshes(-1), m_NumJoints(-1), m_Meshes(0), m_BindingPose(0) {}

md5_model::~md5_model(){
	delete[] m_BindingPose;
	delete[] m_Meshes;
}

void md5_model::validate(){
	if(m_NumMeshes == -1){
		throw md5_node_exception("No numMeshes specified");
	}

	if(m_NumJoints == -1){
		throw md5_node_exception("No numJoints specified");
	}

	for(int i = 0; i < m_NumMeshes; i++){
		m_Meshes[i].validate();
	}

	for(int i = 0; i < m_NumJoints; i++){
		m_BindingPose[i].validate();
	}
}
